/*
 * MIT License
 *
 * Copyright (c) 2023 北京凯特伟业科技有限公司
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.je.message.service.note;

import com.google.common.base.Strings;
import com.je.common.base.DynaBean;
import com.je.common.base.service.MetaService;
import com.je.common.base.service.rpc.SystemSettingRpcService;
import com.je.common.base.service.rpc.SystemVariableRpcService;
import com.je.common.base.util.MessageUtils;
import com.je.common.base.util.SecurityUserHolder;
import com.je.common.base.util.StringUtil;
import com.je.ibatis.extension.conditions.ConditionsWrapper;
import com.je.message.Push;
import com.je.message.cache.NoteTemplateCache;
import com.je.message.exception.MessageSendException;
import com.je.message.rpc.NoteRpcService;
import com.je.message.service.log.LogServiceFactory;
import com.je.message.vo.CodeEnum;
import com.je.message.vo.LogMsg;
import com.je.message.vo.NoteMsg;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.tokenattributes.TermAttribute;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.wltea.analyzer.lucene.IKAnalyzer;
import java.io.IOException;
import java.io.StringReader;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 抽象短信服务实现，用于实现通用方法处理
 */
public abstract class AbstractNoteServiceImpl implements NoteRpcService {

    public static List<String> errorText = new ArrayList<String>();
    public static final String SYSTEM_VAR_NOTETYPE = "JE_SYS_NOTEDIY";

    @Autowired
    private MetaService metaService;
    @Autowired
    private SystemSettingRpcService systemSettingRpcService;
    @Autowired
    private NoteTemplateCache noteTemplateCache;
    @Autowired
    private SystemVariableRpcService systemVariableRpcService;
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 验证码的时效key
     */
    private static final String CAPTCHA_KEY_TEMPLATE = "feedBackCaptcha/%s/%s";
    /**
     * 每天手机号的发送次数key
     */
    private static final String PHONE_KEY = "feedBackPhone/%s";

    /**
     * 短信返回状态码
     * 成功：      200
     * 手机号为空   9000
     * 超过次数     9001
     * 超过时效     9002
     */

    @Override
    public boolean sendNoteMsg(NoteMsg msgVo) {
        chickMsgConfig();
        if (StringUtil.isEmpty(msgVo.getPhoneNumber())) {
            throw new MessageSendException(MessageUtils.getMessage("message.send.phone.canNotEmpty"));
        }

        //格式化关键字
        String sendContext = formatSendText(msgVo.getContext());

        String errorMessage = null;
        int result = 0;
        result = sendNote(msgVo.getPhoneNumber(), sendContext);
        if (result < 0) {
            errorMessage = "发送失败！";
        }
        LogMsg logMsg = new LogMsg();
        logMsg.setFromUser(msgVo.getFromUser());
        logMsg.setFromUserId(msgVo.getFromUserId());
        logMsg.setToUser(msgVo.getToUser());
        logMsg.setToUserId(msgVo.getToUserId());
        logMsg.setErrorMessage(errorMessage);
        logMsg.setContext(sendContext);
        logMsg.setResult(String.valueOf(result));
        BeanUtils.copyProperties(msgVo, logMsg);
        LogServiceFactory.newLog(LogServiceFactory.LogTypeEnum.NOTE).saveLog(logMsg);
        if (result < 0) {
            throw new MessageSendException(MessageUtils.getMessage("message.message.failed.to.send"));
        }
        return true;
    }

    @Override
    public int sendNoteCode(NoteMsg msgVo) {
        Map<String, String> chickMsgConfig = chickMsgConfig();
        if (StringUtil.isEmpty(msgVo.getPhoneNumber())) {
            return CodeEnum._9000.getCode();
        }

        //格式化关键字
        String sendContext = formatSendText(msgVo.getContext());

        String errorMessage = null;
        int result;
        //增加次数，有效时长校验
        //每天次数
        String noteCount = chickMsgConfig.get("noteCount");
        //超时时长
        String timeout = chickMsgConfig.get("timeout");
        String key = String.format(CAPTCHA_KEY_TEMPLATE, msgVo.getPhoneNumber(), msgVo.getMsgCode());
        String phoneKey = String.format(PHONE_KEY, msgVo.getPhoneNumber());

        if (redisTemplate.hasKey(phoneKey) && Integer.parseInt(redisTemplate.opsForValue().get(phoneKey).toString()) >= Integer.parseInt(noteCount)) {
            //超过每天次数
            return CodeEnum._9001.getCode();
        }
        if (redisTemplate.hasKey(key)) {
            //重复发送
            return CodeEnum._9002.getCode();
        }
        result = sendNote(msgVo.getPhoneNumber(), sendContext);
        if (result < 0) {
            errorMessage = "发送失败！";
        }

        String noteType = systemSettingRpcService.findSettingValue(SYSTEM_VAR_NOTETYPE);

        LogMsg logMsg = new LogMsg();
        logMsg.setFromUser(msgVo.getFromUser());
        logMsg.setFromUserId(msgVo.getFromUserId());
        logMsg.setToUser(msgVo.getToUser());
        logMsg.setToUserId(msgVo.getToUserId());
        logMsg.setErrorMessage(errorMessage);
        logMsg.setContext(sendContext);
        logMsg.setResult(String.valueOf(result));
        BeanUtils.copyProperties(msgVo, logMsg);
        logMsg.setFl(noteType);
        LogServiceFactory.newLog(LogServiceFactory.LogTypeEnum.NOTE).saveCodeLog(logMsg);
        if (result < 0) {
            return CodeEnum.FALSE.getCode();
        } else {
            redisTemplate.opsForValue().set(key, msgVo.getMsgCode());
            //验证码时效
            redisTemplate.expire(key, Long.parseLong(timeout), TimeUnit.SECONDS);
            if (redisTemplate.hasKey(phoneKey)) {
                int parseInt = Integer.parseInt(redisTemplate.opsForValue().get(phoneKey).toString());
                redisTemplate.opsForValue().set(phoneKey, parseInt + 1);
            } else {
                redisTemplate.opsForValue().set(phoneKey, 1);
                //验证码时效
                redisTemplate.expire(phoneKey, getTomorrowSeconds(), TimeUnit.SECONDS);
            }
        }

        return CodeEnum.SUCCESS.getCode();
    }

    public Long getTomorrowSeconds() {
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DAY_OF_YEAR, 1);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return (calendar.getTimeInMillis() - System.currentTimeMillis()) / 1000;
    }

    @Override
    public int sendWithRecordSendCode(String phoneNumber, String fromUser, String fromUserId, String toUser, String toUserId, String context) {
        NoteMsg msgVo = new NoteMsg(phoneNumber, fromUser, fromUserId, toUser, toUserId, context);
        return sendNoteCode(msgVo);
    }

    @Override
    public boolean sendWithRecordSendUser(String phoneNumber, String fromUser, String fromUserId, String toUser, String toUserId, String context) {
        NoteMsg msgVo = new NoteMsg(phoneNumber, fromUser, fromUserId, toUser, toUserId, context);
        return sendNoteMsg(msgVo);
    }

    public boolean send(String phoneNumber, String toUser, String toUserId, String context) {
        NoteMsg msgVo = new NoteMsg(phoneNumber, toUser, toUserId, context);
        return sendNoteMsg(msgVo);
    }

    @Override
    public boolean sendSimple(String phoneNumber, String context) {
        NoteMsg msgVo = new NoteMsg(phoneNumber, context);
        return sendNoteMsg(msgVo);
    }

    @Override
    public boolean sendTemplate(String phoneNumber, String tempCode, Map params) {
        String tenantId = SecurityUserHolder.getCurrentAccountTenantId();
        String templateValue;
        if (Strings.isNullOrEmpty(tenantId)) {
            templateValue = noteTemplateCache.getCacheValue(tempCode);
        } else {
            templateValue = noteTemplateCache.getCacheValue(tenantId, tempCode);
        }

        if (StringUtil.isEmpty(templateValue)) {
            DynaBean msgBean = metaService.selectOne("JE_SYS_NOTEBASE", ConditionsWrapper.builder().eq("NOTEBASE_CODE", tempCode));
            templateValue = msgBean.getStr("NOTEBASE_NR");
            if (Strings.isNullOrEmpty(tenantId)) {
                noteTemplateCache.putCache(tempCode, templateValue);
            } else {
                noteTemplateCache.putCache(tenantId, tempCode, templateValue);
            }
        }

        Map<String, Object> ddMap = systemVariableRpcService.formatCurrentUserAndCachedVariables();
        //加入系统设置
        ddMap.putAll(systemSettingRpcService.requireAllSettingValue());
        if (params != null) {
            ddMap.putAll(params);
        }
        templateValue = StringUtil.parseKeyWord(templateValue, ddMap);
        return sendSimple(phoneNumber, templateValue);
    }

    public Map<String, String> chickMsgConfig() {
        Map<String, String> map = new HashMap<>();
        //校验系统设置有没有勾选短信
        String push_platform = systemSettingRpcService.findSettingValue("JE_PUSH_PLATFORM");
        if (!push_platform.contains(Push.NOTE.getCode())) {
            throw new MessageSendException(MessageUtils.getMessage("message.sms.push.is.not.configured"));
        }
        String noteCount = systemSettingRpcService.findSettingValue("JE_SYS_NOTE_COUNT");
        String timeout = systemSettingRpcService.findSettingValue("JE_SMS_TIMEOUT");
        map.put("noteCount", noteCount);
        map.put("timeout", timeout);
        return map;
    }

    public String unicodeToCn(String unicode) {
        /** 以 \ u 分割，因为java注释也能识别unicode，因此中间加了一个空格*/
        String[] strs = unicode.split("\\\\u");
        String returnStr = "";
        // 由于unicode字符串以 \ u 开头，因此分割出的第一个字符是""。
        for (int i = 1; i < strs.length; i++) {
            returnStr += (char) Integer.valueOf(strs[i], 16).intValue();
        }
        return returnStr;
    }


    public String formatSendText(String context) {
        StringReader reader = new StringReader(context);
        Analyzer analyzer = new IKAnalyzer(false);
        TokenStream ts = analyzer.tokenStream("", reader);
        ts.addAttribute(TermAttribute.class);
        while (true) {
            try {
                if (!ts.incrementToken()) {
                    break;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            TermAttribute ta = ts.getAttribute(TermAttribute.class);
            String trim = ta.term();
            if (trim.length() > 1 && errorText.contains(trim)) {
                String result = trim.replaceAll("", "_");
                context = context.replace(trim, result.substring(1, result.length() - 1));
            }
        }
        return context;
    }

    public String checkSendText(String context) throws IOException {
        String error = "";
        StringReader reader = new StringReader(context);
        Analyzer analyzer = new IKAnalyzer(false);
        TokenStream ts = analyzer.tokenStream("", reader);
        ts.addAttribute(TermAttribute.class);
        while (ts.incrementToken()) {
            TermAttribute ta = ts.getAttribute(TermAttribute.class);
            String trim = ta.term();
            if (trim.length() > 1 && errorText.contains(trim)) {
                error = trim;
                break;
            }
        }
        return error;
    }
}
